﻿using System;
using System.Collections.Generic;
using System.Text;
//new
using JRCore.enums;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
using JRCore.protocol;

namespace JRCore.tools
{
    /// <summary>
    /// TCP工作类
    /// </summary>
    public class Server
    {
        private bool m_exit;
        private Enum_TcpWorkMode m_mode;
        private string m_serverip;
        private string m_serverport;
        private string m_secretkey;
        private List<BinaryWriter> m_writerList;
        private List<BinaryReader> m_readerList;
        private TcpListener m_listen;
        private Thread m_ListernThread;//监听线程
        private int m_TimeOut;

        /// <summary>
        /// 服务端消息处理函数，重写本函数实现具体操作
        /// Read/ReadSend/SendRead模式下重写，Send模式下不需要重写
        /// </summary>
        /// <param name="send">Read模式send不使用,ReadSend模式send为输出，SendRead模式send为输入</param>
        /// <param name="read">Read/ReadSend/SendRead模式下，read为对方发送的消息</param>
        /// <param name="error">错误提示信息</param>
        /// <returns>处理结果</returns>
        public delegate bool RecordReceived(ref Msg send,Msg read, ref string error);

        /// <summary>
        /// 服务端消息处理函数，重写本函数实现具体操作
        /// Read/ReadSend/SendRead模式下重写，Send模式下不需要重写
        /// Read模式send不使用,ReadSend模式send为输出，SendRead模式send为输入
        /// Read/ReadSend/SendRead模式下，read为对方发送的消息
        /// </summary>
        public RecordReceived OnRecordReceived;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="serverip">服务端IP</param>
        /// <param name="serverport">服务端PORT</param>
        /// <param name="mode">工作模式</param>
        /// <param name="timeout">读写超时时间（ms），赋值0代表阻塞式读取和发送</param>
        public Server(string serverip, string serverport,Enum_TcpWorkMode mode,int timeout)
        {
            try
            {
                m_TimeOut = timeout;
                m_serverip = serverip;
                m_serverport = serverport;
                m_mode = mode;
                m_secretkey = SecretKey.GetKey(m_serverip);
                m_writerList = new List<BinaryWriter>();
                m_readerList = new List<BinaryReader>();
                OnRecordReceived = new RecordReceived(RecordReceivedFunc);
                //启动监听线程
                m_listen = new TcpListener(IPAddress.Parse(m_serverip), int.Parse(m_serverport));
                m_ListernThread = new Thread(new ParameterizedThreadStart(ListernThreadFunc));
                m_ListernThread.IsBackground = true;
                m_ListernThread.Start(m_listen);
            }
            catch { }
        }

        /// <summary>
        /// 销毁
        /// </summary>
        public void Dispose()
        {
            m_exit = true;

            try
            {
                if (m_writerList != null)
                {
                    foreach (BinaryWriter bw in m_writerList)
                    {
                        bw.Close();
                    }
                    m_writerList.Clear();
                }
            }
            catch { }

            try
            {
                if (m_readerList != null)
                {
                    foreach (BinaryReader br in m_readerList)
                    {
                        br.Close();
                    }
                    m_readerList.Clear();
                }
            }
            catch { }

            try
            {
                if (m_listen != null)
                {
                    m_listen.Stop();
                    if (m_listen.Server != null)
                    {
                        m_listen.Server.Shutdown(SocketShutdown.Both);
                        m_listen.Server.Close();
                    }
                    m_listen = null;
                }
            }
            catch { }

            try
            {
                m_ListernThread.Abort();
                m_ListernThread.Join(3000);
            }
            catch { }

            GC.Collect();
        }

        private void ListernThreadFunc(object obj)
        {
            TcpListener listen = (TcpListener)obj;
            bool flag = false;//监听是否建立
            bool shown = false;
            while (!m_exit)
            {
                try
                {
                    if (flag == false)
                    {
                        try
                        {
                            listen.Start();
                            flag = true;
                            Console.WriteLine("启动监听");
                        }
                        catch (SocketException)
                        {
                            flag = false;
                            if (shown == false)
                            {
                                Console.WriteLine("监听失败");
                                shown = true;
                            }
                        }
                    }

                    if ((flag == true) && (listen.Pending() == true))
                    {
                        TcpClient tcpclient = listen.AcceptTcpClient();
                        if (m_TimeOut > 0)
                        {
                            tcpclient.SendTimeout = m_TimeOut;
                            tcpclient.ReceiveTimeout = m_TimeOut;
                        }
                        Thread DealTcpClientThread = new Thread(new ParameterizedThreadStart(DealTcpClientThreadFunc));
                        DealTcpClientThread.IsBackground = false;
                        DealTcpClientThread.Start(tcpclient);
                    }
                }
                catch (ThreadAbortException)
                {
                    Thread.ResetAbort();
                }
                catch (Exception er)
                {
                    Console.WriteLine("Server.ListernThreadFunc()" + er.Message);
                }
                Thread.Sleep(1000);
            }
        }

        private void DealTcpClientThreadFunc(object obj)
        {
            //TCP
            TcpClient tcpclient = (TcpClient)obj;
            IPEndPoint ipe = (IPEndPoint)tcpclient.Client.RemoteEndPoint;
            NetworkStream ns = tcpclient.GetStream();
            BinaryReader br = new BinaryReader(ns, Encoding.UTF8);
            BinaryWriter bw = new BinaryWriter(ns, Encoding.UTF8);
            if (m_writerList != null) m_writerList.Add(bw);
            if (m_readerList != null) m_readerList.Add(br);
            Msg read = new Msg();
            Msg send = new Msg();
            byte[] buffer = null;
            try
            {
                Console.WriteLine("建立连接" + ipe.Address.ToString() + ":" + ipe.Port.ToString());
                while ((m_exit == false) && (tcpclient.Client.Connected == true))
                {
                    bool feedback = false;
                    string error = "";
                    bool flag = false;
                    
                    switch (m_mode)
                    {
                        case Enum_TcpWorkMode.Read:
                            read.type = (Enum_ProtocolType)br.ReadByte();
                            read.uuid = Encoding.UTF8.GetString(br.ReadBytes(32));
                            feedback = (br.ReadByte() == 0) ? false : true;
                            read.feedback = feedback;
                            read.length = br.ReadInt32();
                            read.data = br.ReadBytes(read.length);
                            flag = OnRecordReceived.Invoke(ref send, read, ref error);
                            break;
                        case Enum_TcpWorkMode.ReadSend:
                            read.type = (Enum_ProtocolType)br.ReadByte();
                            read.uuid = Encoding.UTF8.GetString(br.ReadBytes(32));
                            feedback = (br.ReadByte() == 0) ? false : true;
                            read.feedback = feedback;
                            read.length = br.ReadInt32();
                            read.data = br.ReadBytes(read.length);
                            flag = OnRecordReceived.Invoke(ref send, read, ref error);
                            if ((feedback) && (send != null) && (send.data != null))
                            {
                                buffer = new byte[1 + 32 + 1 + 4 + send.length];
                                buffer[0] = (byte)send.type;
                                Array.Copy(Encoding.UTF8.GetBytes(send.uuid), 0, buffer, 1, 32);
                                buffer[33] = (send.feedback ? (byte)1 : (byte)0);
                                Array.Copy(IntToBytes(send.length), 0, buffer, 34, 4);
                                Array.Copy(send.data, 0, buffer, 38, send.length);
                                lock (bw)
                                {
                                    bw.Write(buffer);
                                }
                            }
                            break;
                        case Enum_TcpWorkMode.Send:
                            Thread.Sleep(100);
                            break;
                        case Enum_TcpWorkMode.SendRead:
                            Thread.Sleep(100);
                            break;
                        default:
                            break;
                    }
                    GC.Collect();
                }
            }
            catch (EndOfStreamException)
            {
                ;//unable to read beyond the end of stream
            }
            catch (IOException)
            {
                ;
            }
            catch (SocketException)
            {
                ;
            }
            catch (ThreadAbortException)
            {
                Thread.ResetAbort();
            }
            catch (Exception er)
            {
                Console.WriteLine("Server.DealTcpClientThreadFunc()" + er.Message);
            }
            finally
            {
                if (br != null) br.Close();
                if (bw != null) bw.Close();
                if (ns != null)
                {
                    ns.Close();
                    ns.Dispose();
                }
                if (tcpclient != null)
                {
                    if (tcpclient.Client != null)
                    {
                        if (tcpclient.Client.Connected == true)
                        {
                            tcpclient.Client.Shutdown(SocketShutdown.Both);
                            tcpclient.Client.Close();
                        }
                    }
                    tcpclient.Close();
                }
                if (m_writerList!=null)m_writerList.Remove(bw);
                if (m_readerList != null) m_readerList.Remove(br);
                 Console.WriteLine("连接断开" + ipe.Address.ToString() + ":" + ipe.Port.ToString());
            }
        }

        private bool RecordReceivedFunc(ref Msg send, Msg read , ref string error)
        {
            error = "没有实现该消息处理函数";
            return false;
        }

        /// <summary>
        /// 推送一组消息到所有连接
        /// </summary>
        /// <param name="msg">消息</param>
        /// <param name="error">错误提示信息</param>
        /// <returns>结果</returns>
        public bool Push(Msg msg, ref string error)
        {
            bool ans = false;
            byte[] buffer = null;
            Msg feedback = null;
            try
            {
                switch (m_mode)
                {
                    case Enum_TcpWorkMode.Read:
                        error = "Read模式不能使用Push()";
                        break;
                    case Enum_TcpWorkMode.ReadSend:
                        error = "ReadWrite模式不能使用Push()";
                        break;
                    case Enum_TcpWorkMode.Send:
                        if ((msg != null)&&(msg.feedback == false))
                        {
                            if ((m_writerList != null) && (m_writerList.Count > 0))
                            {
                                if ((msg.data != null) && (msg.data.Length > 0))
                                {
                                    foreach (BinaryWriter bw in m_writerList)
                                    {
                                        #region 推送到一个客户端
                                        try
                                        {
                                            lock (bw)
                                            {
                                                buffer = new byte[1 + 32 + 1 + 4 + msg.length];
                                                buffer[0] = (byte)msg.type;
                                                Array.Copy(Encoding.UTF8.GetBytes(msg.uuid), 0, buffer, 1, 32);
                                                buffer[33] = (byte)0;
                                                Array.Copy(IntToBytes(msg.length), 0, buffer, 34, 4);
                                                Array.Copy(msg.data, 0, buffer, 38, msg.length);
                                                bw.Write(buffer);
                                                ans = true;
                                                Console.WriteLine("推送" + msg.uuid);
                                            }
                                        }
                                        catch (Exception er)
                                        {
                                            error = "Server.Push()" + er.Message;
                                            Console.WriteLine(error);
                                        }
                                        #endregion
                                    }
                                }
                            }
                        }
                        else
                        {
                            error = "Send模式推送时，消息feedback不能为true";
                        }
                        break;
                    case Enum_TcpWorkMode.SendRead:
                        if ((msg != null)&&(msg.feedback == true))
                        { 
                            if ((m_writerList != null) && (m_writerList.Count > 0) )
                            {
                                if ((msg.data != null) && (msg.data.Length > 0))
                                {
                                    int success = 0;

                                    for (int i = 0; i < m_writerList.Count; i++)
                                    {
                                        #region 推送到一个客户端
                                        try
                                        {
                                            lock (m_writerList[i])
                                            {
                                                buffer = new byte[1 + 32 + 1 + 4 + msg.length];
                                                buffer[0] = (byte)msg.type;
                                                Array.Copy(Encoding.UTF8.GetBytes(msg.uuid), 0, buffer, 1, 32);
                                                buffer[33] = (byte)1;
                                                Array.Copy(IntToBytes(msg.length), 0, buffer, 34, 4);
                                                Array.Copy(msg.data, 0, buffer, 38, msg.length);
                                                m_writerList[i].Write(buffer);
                                            
                                                feedback = new Msg();
                                                feedback.type = (Enum_ProtocolType)m_readerList[i].ReadByte();
                                                feedback.uuid = Encoding.UTF8.GetString(m_readerList[i].ReadBytes(32));
                                                feedback.feedback = (m_readerList[i].ReadByte() == 0) ? false : true;
                                                feedback.length = m_readerList[i].ReadInt32();
                                                feedback.data = m_readerList[i].ReadBytes(feedback.length);
                                            }

                                            if ((msg.type == Enum_ProtocolType.PicCenterPush) && (feedback.type == Enum_ProtocolType.PicCenterPushResponse))
                                            {
                                                if (msg.uuid == feedback.uuid)
                                                {
                                                    PicCenterPush record = PicCenterPush.Parse(msg.data);
                                                    PicCenterPushFeedBack back = PicCenterPushFeedBack.Parse(feedback.data);
                                                    if ((record != null) && (back != null))
                                                    {
                                                        if (back.P2 == "1")
                                                        {
                                                            success++;
                                                        }
                                                        else if (back.P2 == "0")
                                                        {
                                                            error = back.P3;
                                                        }
                                                    }
                                                }
                                                else
                                                {
                                                    error = "uuid不一致";
                                                }
                                            }
                                            else
                                            {
                                                error = "消息类型错误";
                                            }
                                            Console.WriteLine("推送" + msg.uuid);
                                        }
                                        catch (Exception er)
                                        {
                                            error = "Server.Push()" + er.Message;
                                            Console.WriteLine(error);
                                        }
                                        #endregion
                                    }
                                    ans = (success == m_writerList.Count);
                                }
                            }
                        }
                        else
                        {
                            error = "SendRead模式推送时，消息feedback不能为false";
                        }
                        break;
                    default:
                        break;
                }
                GC.Collect();

            }
            catch (Exception er)
            {
                ans = false;
                error = "Server.Push()" + er.Message;
                Console.WriteLine(error);
            }
            return ans;
        }

        private static byte[] IntToBytes(int value)
        {
            byte[] src = new byte[4];
            src[3] = (byte)((value >> 24) & 0xFF);
            src[2] = (byte)((value >> 16) & 0xFF);
            src[1] = (byte)((value >> 8) & 0xFF);
            src[0] = (byte)(value & 0xFF);
            return src;
        }
    }
}
